
package com.hxkj.service.impl;


import com.hxkj.client.service.UserServiceClient;
import com.hxkj.domain.enums.UserExceptionEnum;
import com.hxkj.domain.po.AuthRole;
import com.hxkj.domain.po.AuthUser;
import com.hxkj.domain.po.FunctionMenu;
import com.hxkj.domain.po.FunctionSite;
import com.hxkj.service.UserService;
import com.hxkj.token.TokenGenerator;
import com.hxkj.token.po.LoginToken;
import com.hxkj.token.utils.TokenUtils;
import com.platform.common.domain.exception.CustomException;
import com.platform.common.domain.exception.CustomMessageException;
import com.platform.common.domain.result.ResultDataStruct;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * 用户service
 * <p>
 * Create by chenwei on 2018/5/23 15:19
 */
@Service
@Slf4j
public class UserServiceImpl implements UserService {
    @Autowired
    private UserServiceClient userServiceClient;

    @Autowired
    private TokenUtils<AuthUser> tokenUtils;

    @Autowired
    private TokenGenerator generator;

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 用户登录次数计数  redisKey 前缀
     */
    private String SHIRO_LOGIN_COUNT = "shiro:login:count:";
    /**
     * 用户登录是否被锁定    一小时 redisKey 前缀
     */
    private String SHIRO_IS_LOCK = "shiro:is:lock:";

    @Override
    public LoginToken login(AuthUser authUser, String code, String uuid) throws Exception {
        ResultDataStruct<LoginToken> authUserResultDataStruct;
        String userAccount = authUser.getUserAccount();
        // 验证码和登陆次数验证
        checkVerificationCode(userAccount, code, uuid);
        authUserResultDataStruct = userServiceClient.login(authUser);
        if (authUserResultDataStruct.getCode() == 200) {
            resetAccountVerificationCodeCheck(userAccount, uuid);
            return authUserResultDataStruct.getData();
        } else {
            throw new CustomMessageException(authUserResultDataStruct.getCode(), authUserResultDataStruct.getMessage());
        }

    }


    /**
     * 验证码检查
     *
     * @param userAccount
     * @param code
     * @param uuid
     * @throws Exception
     */
    private void checkVerificationCode(String userAccount, String code, String uuid) throws Exception {
        if (!redisTemplate.hasKey(SHIRO_LOGIN_COUNT + userAccount)) {
            redisTemplate.boundValueOps(SHIRO_LOGIN_COUNT + userAccount).set(0L);
        }

        if (!redisTemplate.hasKey(SHIRO_IS_LOCK + userAccount)) {
            redisTemplate.boundValueOps(SHIRO_IS_LOCK + userAccount).set(null);
        }
        Long loginCount = (long) redisTemplate.boundValueOps(SHIRO_LOGIN_COUNT + userAccount).get();

        if (StringUtils.isBlank(code)) {
            throw new CustomException(UserExceptionEnum.REQUIRE_VALIDATECODE);
        }
        // 初始化数据
        if (redisTemplate.boundValueOps(SHIRO_LOGIN_COUNT + userAccount).get() == null) {
            loginCount = 0L;
        }
        if (redisTemplate.boundValueOps(SHIRO_IS_LOCK + userAccount).get() == null) {
            redisTemplate.boundValueOps(SHIRO_IS_LOCK + userAccount).set("UNLOCK");
        }
        // 验证码错误次数大于5,用户账户锁定10分钟
        if (loginCount >= 5) {
            redisTemplate.boundValueOps(SHIRO_IS_LOCK + userAccount).set("LOCK");
            redisTemplate.boundValueOps(SHIRO_IS_LOCK + userAccount).expire(1, TimeUnit.MINUTES);
            redisTemplate.boundValueOps(SHIRO_LOGIN_COUNT + userAccount).set(0L);
        }
        if ("LOCK".equals(redisTemplate.boundValueOps(SHIRO_IS_LOCK + userAccount).get())) {
            throw new CustomException(UserExceptionEnum.ACCOUNT_IS_LOCK);
        }
        // 获取Redis中存储的验证码
        String remoteCode = (String) redisTemplate.boundValueOps(uuid).get();
        if (!code.equalsIgnoreCase(remoteCode)) {
            loginCount++;
            redisTemplate.boundValueOps(SHIRO_LOGIN_COUNT + userAccount).set(loginCount);
            throw new CustomException(UserExceptionEnum.VALIDATECODE_IS_ERROR);
        }
    }


    /**
     * 重置账号验证码检查
     *
     * @param userAccount
     * @throws Exception
     */
    private void resetAccountVerificationCodeCheck(String userAccount, String uuid) throws Exception {
        redisTemplate.boundValueOps(SHIRO_LOGIN_COUNT + userAccount).set(0L);
        redisTemplate.boundValueOps(SHIRO_IS_LOCK + userAccount).set("UNLOCK");
        redisTemplate.delete(uuid);

    }

    @Override
    public AuthUser findByAccount(String account) throws Exception {
        try {
            ResultDataStruct<AuthUser> authUserResultDataStruct = userServiceClient.getUser(account);
            if (authUserResultDataStruct.getCode() == 200) {
                return authUserResultDataStruct.getData();
            } else {
                throw new CustomException(UserExceptionEnum.getEnumByCode(authUserResultDataStruct.getCode()));
            }
        } catch (CustomException e) {
            throw e;
        } catch (Exception e) {
            log.error("用户:{account},请求登录服务失败:{}");
            throw new CustomException(UserExceptionEnum.USER_SERVICE_REQUEST_FAIL);
        }
    }

    @Override
    public Set<String> findRolesByAccount(String account) {
        return null;
    }

    @Override
    public Set<String> findPermissionsByAccount(String account) {
        return null;
    }

    @Override
    public List<FunctionMenu> findFunctionMenuByAccount(String siteId) throws Exception {
        ResultDataStruct<List<FunctionMenu>> listResultDataStruct;
        AuthUser authUser = tokenUtils.getTokenUserInfo();
        FunctionSite functionSite = new FunctionSite();
        functionSite.setSiteId(siteId);
        functionSite.setUserAccount(authUser.getUserAccount());
        listResultDataStruct = userServiceClient.findFunctionMenuByAccount(functionSite);
        if (listResultDataStruct.getCode() == 200) {
            return listResultDataStruct.getData();
        }
        throw new CustomMessageException(listResultDataStruct.getCode(), listResultDataStruct.getMessage());
    }

    @Override
    public List<AuthRole> getRolesByAccount(String appKey, String account) {
        return null;
    }

    @Override
    public void loginOut(String userId) throws Exception {
        generator.generatorOut();
        tokenUtils.invalidUserInfo(userId);
    }

    @Override
    public AuthUser getUserInfo() throws Exception {
        AuthUser authUser = tokenUtils.getTokenUserInfo();
        AuthUser result = new AuthUser();
        result.setUserName(authUser.getUserName());
        result.setUserId(authUser.getUserId());
        return result;
    }


    @Override
    public AuthUser getUserInfoByToken(String accessToken) throws Exception {
        ResultDataStruct<AuthUser> authUserResultDataStruct;
        try {
            authUserResultDataStruct = userServiceClient.getUserInfoByToken(accessToken);
            if (authUserResultDataStruct.getCode() == 200) {
                return authUserResultDataStruct.getData();
            } else {
                throw new CustomException(UserExceptionEnum.getEnumByCode(authUserResultDataStruct.getCode()));
            }
        } catch (CustomException e) {
            throw e;
        } catch (Exception e) {
            e.printStackTrace();
            throw new CustomException(UserExceptionEnum.GET_USER_BY_TOKEN_FAIL);
        }
    }

    @Override
    public AuthUser getUserInfoById(String userId) throws Exception {
        ResultDataStruct<AuthUser> authUserResultDataStruct;
        try {
            authUserResultDataStruct = userServiceClient.getUserInfoById(userId);
            if (authUserResultDataStruct.getCode() == 200) {
                return authUserResultDataStruct.getData();
            } else {
                throw new CustomException(UserExceptionEnum.getEnumByCode(authUserResultDataStruct.getCode()));
            }
        } catch (CustomException e) {
            throw e;
        } catch (Exception e) {
            throw new CustomException(UserExceptionEnum.GET_USER_BY_ID_FAIL);
        }
    }
}
